// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WIMAGE_H_
#define WIMAGE_H_

#include <Wt/WInteractWidget.h>
#include <Wt/WLink.h>

namespace Wt {

  namespace Impl {
    class MapWidget;
  }

  class WAbstractArea;

/*! \class WImage Wt/WImage.h Wt/WImage.h
 *  \brief A widget that displays an image.
 *
 * The image may be specified either as a URL, or may be dynamically
 * generated by a WResource.
 *
 * You may listen to events by attaching event listeners to signals
 * such as clicked(). Since mouse events pass the coordinates through
 * a WMouseEvent object, it is possible to react to clicks in specific
 * parts of the image. An alternative is to define interactive areas
 * on the image using addArea(), which in addition allows to have
 * customized tool tips for certain image areas (using
 * WAbstractArea::setToolTip()).
 *
 * \if cpp
 * Usage example:
 * \code
 * Wt::WImage *img = addWidget(std::make_unique<Wt::WImage>("images/johnny_cash.png"));
 * img->setAlternateText("Johnny Cash sings a song");
 * \endcode
 * \endif
 *
 * %WImage is an \link WWidget::setInline(bool) inline \endlink widget.
 *
 * <h3>CSS</h3>
 *
 * The widget corresponds to the HTML <tt>&lt;img&gt;</tt> tag and
 * does not provide styling. It can be styled using inline or external
 * CSS as appropriate.
 *
 * \sa WResource, WPaintedWidget
 */
class WT_API WImage : public WInteractWidget
{
public:
  /*! \brief Creates an empty image widget.
   */
  WImage();

  /*! \brief Creates an image widget with a given image link.
   *
   * The \p imageLink may link to a URL or resource.
   */
  WImage(const WLink& imageLink);

  /*! \brief Creates an image widget with a given image link and alternate text.
   *
   * The \p imageLink may link to a URL or resource.
   */
  WImage(const WLink& imageLink, const WString& altText);

  ~WImage();

  /*! \brief Sets an alternate text.
   *
   * The alternate text should provide a fallback for browsers that do
   * not display an image. If no sensible fallback text can be
   * provided, an empty text is preferred over nonsense.
   *
   * This should not be confused with toolTip() text, which provides
   * additional information that is displayed when the mouse hovers
   * over the image.
   *
   * The default alternate text is an empty text ("").
   *
   * \sa alternateText()
   */
  void setAlternateText(const WString& text);

  /*! \brief Returns the alternate text.
   *
   * \sa setAlternateText()
   */
  const WString& alternateText() const { return altText_; }

  /*! \brief Sets the image link.
   *
   * The image may be specified as a URL or as a resource. A resource
   * specifies application-dependent content, which may be used to
   * generate an image on demand.
   */
  void setImageLink(const WLink& link);

  /*! \brief Returns the image link.
   *
   * \sa setLink()
   */
  const WLink& imageLink() const { return imageLink_; }

  /*! \brief Adds an interactive area.
   *
   * Adds the \p area which listens to events in a specific region
   * of the image. Areas are organized in an indexed list, to which
   * the given \p area is appended. When areas overlap, the area
   * with the lowest index receives the event.
   *
   * Ownership of the \p area is transferred to the image.
   *
   * \sa insertArea(int, WAbstractArea *)
   *
   * \note Currently it is not possible to add a first area after the image
   *       has been rendered. If you want to use interactive areas you need
   *       to add one immediately.
   */
  void addArea(std::unique_ptr<WAbstractArea> area);

  /*! \brief Adds an interactive area, returning a raw pointer.
   *
   * This is implemented as:
   *
   * \code
   * Area *result = area.get();
   * addArea(std::unique_ptr<WAbstractArea>(std::move(area)));
   * return result;
   * \encode
   */
  template <typename Area>
    Area *addArea(std::unique_ptr<Area> area)
  {
    Area *result = area.get();
    addArea(std::unique_ptr<WAbstractArea>(std::move(area)));
    return result;
  }

  /*! \brief Inserts an interactive area.
   *
   * Inserts the \p area which listens to events in the
   * coresponding area of the image. Areas are organized in a list,
   * and the <i>area</i> is inserted at index \p index. When areas
   * overlap, the area with the lowest index receives the event.
   *
   * Ownership of the \p area is transferred to the image.
   *
   * \sa addArea(std::unique_ptr<WAbstractArea>);
   *
   * \note Currently it is not possible to add a first area after the image
   *       has been rendered. If you want to use interactive areas you need
   *       to add one immediately.
   */
  void insertArea(int index, std::unique_ptr<WAbstractArea> area);

  /*! \brief Inserts an interactive area, returning a raw pointer.
   *
   * This is implemented as:
   * 
   * \code
   * Area *result = area.get();
   * insertArea(index, std::unique_ptr<WAbstractArea>(std::move(area)));
   * return result;
   * \encode
   */
  template <typename Area>
    Area *insertArea(int index, std::unique_ptr<Area> area)
  {
    Area *result = area.get();
    insertArea(index, std::unique_ptr<WAbstractArea>(std::move(area)));
    return result;
  }

  /*! \brief Removes an interactive area.
   *
   * Removes the \p area from this widget.
   *
   * \sa addArea(WAbstractArea *)
   */
  std::unique_ptr<WAbstractArea> removeArea(WAbstractArea *area);

  /*! \brief Returns the interactive area at the given index.
   *
   * Returns \c 0 if \p index was invalid.
   *
   * \sa insertArea(int, WAbstractArea *)
   */
  WAbstractArea *area(int index) const;

  /*! \brief Returns the interactive areas set for this widget.
   *
   * \sa addArea()
   */
  const std::vector<WAbstractArea *> areas() const;

  /*! \brief Event emitted when the image was loaded.
   */
  EventSignal<>& imageLoaded();

  void setTargetJS(std::string targetJS);
  virtual std::string updateAreasJS();
  virtual std::string setAreaCoordsJS();

private:
  static const char *LOAD_SIGNAL;

  static const int BIT_ALT_TEXT_CHANGED = 0;
  static const int BIT_IMAGE_LINK_CHANGED = 1;
  static const int BIT_MAP_CREATED = 2;

  WString altText_;
  WLink imageLink_;
  std::unique_ptr<Impl::MapWidget> map_;
  std::bitset<3> flags_;
  std::string targetJS_;

  void resourceChanged();

protected:
  virtual void getDomChanges(std::vector<DomElement *>& result,
                             WApplication *app) override;
  virtual void updateDom(DomElement& element, bool all) override;
  virtual void defineJavaScript();
  virtual void render(WFlags<RenderFlag> flags) override;
  virtual DomElementType domElementType() const override;
  virtual void propagateRenderOk(bool deep) override;
  virtual std::string updateAreaCoordsJSON() const;

  friend class WLabel;
  friend class Impl::MapWidget;
};

}

#endif // WIMAGE_H_
